package net.cloudcodex.server;

import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;

import net.cloudcodex.server.data.Data;

import com.google.appengine.api.datastore.Key;

/**
 * Execution context.
 * 
 * @author Thomas
 */
public class Context {

	/**
	 * Current user.
	 */
	private final Data.User user;
	
	/**
	 * Entity cache.
	 */
	private final Map<Key, Data> cache = new HashMap<Key, Data>();

	/**
	 * Errors.
	 */
	private final Set<String> errors = new LinkedHashSet<String>();
	
	/**
	 * Create the context. FIXME not really good ...
	 * @param user current user.
	 */
	public Context(Data.User user) {
		this.user = user;
	}
	
	/**
	 * Create the context. FIXME not really good ...
	 * @param user current user.
	 */
	public Context(Context context, Data.User user) {
		if(context != null) {
			this.cache.putAll(context.cache);
		}
		this.user = user;
	}
	
	/**
	 * @return the user
	 */
	public Data.User getUser() {
		return user;
	}

	/**
	 * @return the cache
	 */
	public Map<Key, Data> getCache() {
		return cache;
	}

	/**
	 * Search a data in cache.
	 * @param key key of the data.
	 * @return the data in cache if found.
	 */
	public Data getData(Key key) {
		return key == null ? null : cache.get(key);
	}

	/**
	 * Put a data in cache.
	 * @param data to put in cache.
	 */
	public void cache(Data data) {
		if(data != null) {
			cache.put(data.getKey(), data);
		}
	}
	
	/**
	 * Removes a data from cache.
	 * @param data to remove from cache.
	 */
	public void uncache(Data data) {
		if(data != null) {
			cache.remove(data.getKey());
		}
	}

	/**
	 * Removes a data from cache.
	 * @param key key of the data to remove from cache.
	 */
	public void uncache(Key key) {
		if(key != null) {
			cache.remove(key);
		}
	}
	
	/**
	 * Removes a data from cache.
	 * @param keys keys of the data to remove from cache.
	 */
	public void uncache(Collection<Key> keys) {
		if(keys != null) {
			for(Key key : keys) {
				cache.remove(key);
			}
		}
	}


	/**
	 * Add an error to the context.
	 * @param error error to add.
	 */
	public void addError(String error) {
		if(error != null) {
			errors.add(error);
		}
	}
	
	/**
	 * Add an error to the context.
	 * @param error error to add.
	 * @param params params of the error.
	 */
	public void addError(String error, Object... params) {
		if(error != null) {
			final StringBuffer buffer = new StringBuffer(error);
			if(params != null && params.length > 0) {
				buffer.append(":");
				for(int n = 0; n < params.length; n++) {
					if(n > 0) {
						buffer.append(",");
					}
					buffer.append(params[n]);
				}
			}
			errors.add(buffer.toString());
		}
	}
	
	/**
	 * @return <code>true</code> if errors have been registered.
	 */
	public boolean hasErrors() {
		return !errors.isEmpty();
	}

	/**
	 * @return the registrered errors
	 */
	public Set<String> getErrors() {
		return errors;
	}

	/**
	 * @param code an error code or a part of one.
	 * @return <code>true</code> if the context contains this error code
	 * 		or an error code starting with it.
	 */
	public boolean hasError(String code) {
		for(String error : errors) {
			if(error.startsWith(code)) {
				return true;
			}
		}
		return false;
	}
}
